#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <errno.h>
#include <sys/stat.h>

/*
 /var/tmp linux临时目录, 默认清理30天未使用文件
 /tmp 目录默认清理10天未使用文件, 系统重启会清空目录
*/
#define CLI_PATH         "/var/tmp/"

/**
 * 客户进程向服务器进程发起连接请求
 * 成功返回客户进程sock fd
 */
int cli_conn(const char *name) {
    int fd, len;
    int ret;
    struct sockaddr_un un;

    // UDP-SOCK_DGRAM无需建立连接
    // TCP-SOCK_STREAM需要建立连接
    fd = socket(AF_UNIX, SOCK_STREAM, 0);
    if (fd < 0) {
        return -1;
    }

    // 确保sun_path有足够空间存放name + null终结符
    if (strlen(name) > sizeof(un.sun_path) + 5 - 1) {
        errno = ENAMETOOLONG;
        return -1;
    }

    memset(&un, 0, sizeof (un));
    un.sun_family = AF_UNIX;
    sprintf(un.sun_path, "%s%5ld", CLI_PATH, (long)getpid());
    
    len = offsetof(struct sockaddr_un, sun_path) + strlen(un.sun_path);

    // 如何指定路径存在文件, 就移除. 避免对bind创建sock文件产生影响
    unlink(un.sun_path);
    
    // 绑定地址信息(域 + name), 创建sock文件
    ret = bind(fd, (struct sockaddr *)&un, len);
    if (ret < 0) {
        close(fd);
        return -2;
    }

    // 修改sock文件权限为rwx------
    ret = chmod(un.sun_path, S_IRWXU);
    if (ret < 0) {
        close(fd);
        unlink(un.sun_path);
        return -3;
    }
    
    struct sockaddr_un servun;
    int servlen;

    memset(&servun, 0, sizeof (servun));
    servun.sun_family = AF_UNIX;
    strcpy(servun.sun_path, name);
    servlen = offsetof(struct sockaddr_un, sun_path) + strlen(name);

    // 请求连接服务器
    ret = connect(fd, (struct sockaddr *)&servun, servlen);
    if (ret < 0) {
        close(fd);
        unlink(un.sun_path);
        return -4;
    }

    return fd;
}

